/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package studio.raptor.ddal.benchmark.utils;

import static io.airlift.units.DataSize.Unit.BYTE;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.SECONDS;

import io.airlift.units.DataSize;
import io.airlift.units.Duration;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public final class FormatUtils {
  private FormatUtils() {
  }

  public static String formatCount(long count) {
    double fractional = count;
    String unit = "";
    if (fractional > 1000) {
      fractional /= 1000;
      unit = "K";
    }
    if (fractional > 1000) {
      fractional /= 1000;
      unit = "M";
    }
    if (fractional > 1000) {
      fractional /= 1000;
      unit = "B";
    }
    if (fractional > 1000) {
      fractional /= 1000;
      unit = "T";
    }
    if (fractional > 1000) {
      fractional /= 1000;
      unit = "Q";
    }

    return format("%s%s", getFormat(fractional).format(fractional), unit);
  }

  static String formatCountRate(double count, Duration duration, boolean longForm) {
    double rate = count / duration.getValue(SECONDS);
    if (Double.isNaN(rate) || Double.isInfinite(rate)) {
      rate = 0;
    }

    String rateString = formatCount((long) rate);
    if (longForm) {
      if (rateString.endsWith(" ")) {
        rateString = rateString.substring(0, rateString.length() - 1);
      }
      rateString += "/s";
    }
    return rateString;
  }

  private static String formatDataSize(DataSize size, boolean longForm) {
    double fractional = size.toBytes();
    String unit = null;
    if (fractional >= 1024) {
      fractional /= 1024;
      unit = "K";
    }
    if (fractional >= 1024) {
      fractional /= 1024;
      unit = "M";
    }
    if (fractional >= 1024) {
      fractional /= 1024;
      unit = "G";
    }
    if (fractional >= 1024) {
      fractional /= 1024;
      unit = "T";
    }
    if (fractional >= 1024) {
      fractional /= 1024;
      unit = "P";
    }

    if (unit == null) {
      unit = "B";
    } else if (longForm) {
      unit += "B";
    }

    return format("%s%s", getFormat(fractional).format(fractional), unit);
  }

  public static String formatDataRate(DataSize dataSize, Duration duration, boolean longForm) {
    double rate = dataSize.toBytes() / duration.getValue(SECONDS);
    if (Double.isNaN(rate) || Double.isInfinite(rate)) {
      rate = 0;
    }

    String rateString = formatDataSize(new DataSize(rate, BYTE), false);
    if (longForm) {
      if (!rateString.endsWith("B")) {
        rateString += "B";
      }
      rateString += "/s";
    }
    return rateString;
  }

  private static DecimalFormat getFormat(double value) {
    DecimalFormat format;
    if (value < 10) {
      // show up to two decimals to get 3 significant digits
      format = new DecimalFormat("#.##");
    } else if (value < 100) {
      // show up to one decimal to get 3 significant digits
      format = new DecimalFormat("#.#");
    } else {
      // show no decimals -- we have enough digits in the integer part
      format = new DecimalFormat("#");
    }

    format.setRoundingMode(RoundingMode.HALF_UP);
    return format;
  }
}
